import os
import fnmatch
import argparse
from pathlib import Path
from typing import List, Set

def parse_gitignore(gitignore_path: Path) -> Set[str]:
    """解析.gitignore文件，返回忽略规则的集合"""
    ignore_patterns = set()
    if not gitignore_path.exists():
        print(f".gitignore 文件 {gitignore_path} 不存在，将不忽略任何文件（除默认忽略外）")
        return ignore_patterns

    with open(gitignore_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line and not line.startswith('#'):
                ignore_patterns.add(line)
    return ignore_patterns

def should_ignore(file_path: Path, ignore_patterns: Set[str], root_dir: Path) -> bool:
    """判断文件是否应该被忽略"""
    rel_path = str(file_path.relative_to(root_dir))
    
    for pattern in ignore_patterns:
        if pattern.endswith('/'):
            pattern = pattern.rstrip('/')
            if fnmatch.fnmatch(rel_path, f"{pattern}/*"):
                return True
        elif fnmatch.fnmatch(rel_path, pattern) or fnmatch.fnmatch(rel_path, f"*/{pattern}"):
            return True
        elif 'node_modules' in rel_path or 'dist' in rel_path:
            return True
    return False

def collect_target_files(source_dir: Path, ignore_patterns: Set[str]) -> List[Path]:
    """收集指定目录下所有未被忽略的目标文件（.py, .yaml, .yml, .vue, .js, .ts, .txt, Dockerfile）"""
    target_files = []
    root_dir = source_dir

    for root, dirs, files in os.walk(source_dir):
        current_path = Path(root)
        
        # 过滤掉需要忽略的目录
        dirs[:] = [d for d in dirs if not should_ignore(current_path / d, ignore_patterns, root_dir)]
        
        for file in files:
            file_path = Path(root) / file
            # 检查文件是否为目标类型，且不被忽略
            if (file.endswith(('.py', '.yaml', '.yml', '.txt', '.vue', '.js', '.ts')) or file == 'Dockerfile') and not should_ignore(file_path, ignore_patterns, root_dir):
                target_files.append(file_path)
    
    return target_files

def generate_directory_tree(source_dir: Path, ignore_patterns: Set[str]) -> str:
    """生成目录树结构描述"""
    tree_lines = []
    root_dir = source_dir

    def _walk_directory(current_path: Path, prefix: str = ""):
        # 获取当前目录下的子目录和文件
        entries = sorted(current_path.iterdir())
        entries = [e for e in entries if not should_ignore(e, ignore_patterns, root_dir)]
        
        for i, entry in enumerate(entries):
            is_last = i == len(entries) - 1
            connector = "└── " if is_last else "├── "
            tree_lines.append(f"{prefix}{connector}{entry.name}")
            
            if entry.is_dir():
                new_prefix = prefix + ("    " if is_last else "│   ")
                _walk_directory(entry, new_prefix)

    tree_lines.append(str(source_dir.name))
    _walk_directory(source_dir)
    return "\n".join(tree_lines)

def append_files_to_output(target_files: List[Path], output_file: Path, source_dir: Path, dir_tree: str):
    """将目录结构和文件内容追加到输出文件中"""
    with open(output_file, 'a', encoding='utf-8') as out_f:
        # 写入目录结构
        out_f.write("项目目录结构:\n")
        out_f.write("```\n")
        out_f.write(dir_tree)
        out_f.write("\n```\n\n")
        
        # 写入文件内容
        for file_path in target_files:
            relative_path = file_path.relative_to(source_dir)
            
            # 写入文件路径
            out_f.write(f"{file_path.name}文件路径: {relative_path}\n")
            out_f.write(f"{file_path.name}文件内容:\n")
            out_f.write("```\n")
            
            # 写入文件内容
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    out_f.write(content)
            except Exception as e:
                out_f.write(f"读取文件时出错: {e}\n")
            
            out_f.write("```\n\n")

def main():
    # 设置命令行参数解析
    parser = argparse.ArgumentParser(description="将指定目录下的目标文件内容追加到输出文件中")
    parser.add_argument("source_dir", type=str, help="源项目目录路径")
    parser.add_argument("--output-file", type=str, default="./output.txt", 
                        help="输出文件路径（默认：./output.txt）")
    parser.add_argument("--gitignore", type=str, 
                        default=str(Path.cwd() / ".gitignore"),
                        help="指定.gitignore文件路径（默认：当前目录下的.gitignore）")

    args = parser.parse_args()

    # 从命令行参数中获取路径
    source_dir = Path(args.source_dir).resolve()
    output_file = Path(args.output_file).resolve()
    gitignore_path = Path(args.gitignore).resolve()

    # 确保源目录存在
    if not source_dir.exists():
        print(f"源目录 {source_dir} 不存在！")
        return

    # 清空或创建输出文件
    if output_file.exists():
        output_file.unlink()  # 删除旧文件
    output_file.touch()  # 创建新文件

    # 解析.gitignore文件
    ignore_patterns = parse_gitignore(gitignore_path)
    
    # 添加默认忽略的目录（如 .git）
    ignore_patterns.add('.git')

    # 收集所有目标文件
    target_files = collect_target_files(source_dir, ignore_patterns)
    
    # 生成目录树
    dir_tree = generate_directory_tree(source_dir, ignore_patterns)
    
    # 将目录树和文件内容追加到输出文件
    append_files_to_output(target_files, output_file, source_dir, dir_tree)
    
    print(f"已将 {len(target_files)} 个目标文件写入 {output_file}")

if __name__ == "__main__":
    main()